/*
 * Copyright (c) 2021 Huawei Device Co.,  Ltd.
 * Licensed under the Apache License,  Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,  software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,  either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "recptprinter_observer.h"
#include <string.h>
#include <uv.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <recptprinter_def.h>
#include "recptprinter_hilog.h"
#include "recptprinter_utils.h"

namespace SwanLink {
namespace Finance {
using namespace OHOS;

PrinterObserver::PrinterObserver() :
    listener_(nullptr) {
}

PrinterObserver::~PrinterObserver() {
    if (listener_) {
        delete listener_;
    }
}

void PrinterObserver::PushObserver(const napi_env &env, const napi_value &listener) {
    std::lock_guard<std::mutex>  lock(mutex_);
    if (listener_) {
        delete listener_;
    }
    listener_ = new NapiPrintListener();
    listener_->env = env;
    napi_value func_succ, func_fail;
    DEBUG_NAPI_CALL_NO_RET(env, napi_get_named_property(env, listener, "OnSuccess", &func_succ));
    DEBUG_NAPI_CALL_NO_RET(env, napi_get_named_property(env, listener, "OnFailed", &func_fail));
    napi_create_reference(env, func_succ, 1, &listener_->func_succ_ref);
    napi_create_reference(env, func_fail, 1, &listener_->func_fail_ref);

    RECPTPRINTER_HILOGD(RECPTPRINTER_JS_NAPI, "observer has been registered");
}

void PrinterObserver::PopObserver() {
    std::lock_guard<std::mutex>  lock(mutex_);
    if (listener_) {
        delete listener_;
    }
    listener_ = nullptr;
}

void PrinterObserver::OnSuccess(int proc, int total) {
    RECPTPRINTER_HILOGD(RECPTPRINTER_JS_NAPI, "proc = %{public}d, total = %{public}d", proc, total);

    std::lock_guard<std::mutex>  lock(mutex_);
    if (listener_ == nullptr) {
        RECPTPRINTER_HILOGE(RECPTPRINTER_JS_NAPI, "observer has been removed");
        return;
    }
    RECPTPRINTER_HILOGE(RECPTPRINTER_JS_NAPI, "observer call success");

    struct tTmpStruct {
        int proc;
        int total;
        void *arg_this;
    };

    tTmpStruct *tmpVar = new tTmpStruct();
    tmpVar->arg_this = listener_;
    tmpVar->proc = proc;
    tmpVar->total = total;

    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(listener_->env, &loop);
    uv_work_t *work = new uv_work_t;
    work->data = (void*)tmpVar;
    uv_queue_work(
        loop,
        work,
        [](uv_work_t *work) {},
        [](uv_work_t *work, int status) {
            tTmpStruct *tmpVar = reinterpret_cast<tTmpStruct *>(work->data);
            NapiPrintListener *listener = reinterpret_cast<NapiPrintListener*>(tmpVar->arg_this);
            listener->OnSuccess(tmpVar->proc, tmpVar->total);
            delete tmpVar;
            delete work;
            work = nullptr;
        }
    );
}
void PrinterObserver::OnFailed(int code, const std::string& err) {
    RECPTPRINTER_HILOGD(RECPTPRINTER_JS_NAPI, "code = %{public}d, err = %{public}s", code, err.c_str());
    std::lock_guard<std::mutex>  lock(mutex_);
    if (listener_ == nullptr) {
        RECPTPRINTER_HILOGE(RECPTPRINTER_JS_NAPI, "observer has been removed");
        return;
    }
    struct tTmpStruct {
        int code;
        std::string err;
        void *arg_this;
    };

    tTmpStruct *tmpVar = new tTmpStruct();
    tmpVar->arg_this = listener_;
    tmpVar->code = code;
    tmpVar->err = err;

    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(listener_->env, &loop);
    uv_work_t *work = new uv_work_t;
    work->data = (void*)tmpVar;
    uv_queue_work(
        loop,
        work,
        [](uv_work_t *work) {},
        [](uv_work_t *work, int status) {
            tTmpStruct *tmpVar = reinterpret_cast<tTmpStruct *>(work->data);
            NapiPrintListener *listener = reinterpret_cast<NapiPrintListener*>(tmpVar->arg_this);
            listener->OnFailed(tmpVar->code, tmpVar->err);
            delete tmpVar;
            delete work;
            work = nullptr;
        }
    );
}

NapiPrintListener::NapiPrintListener() :
    env(nullptr),
    func_succ_ref(nullptr),
    func_fail_ref(nullptr) {
}

NapiPrintListener::~NapiPrintListener() {
    if (func_succ_ref) {
        napi_delete_reference(env, func_succ_ref);
    }

    if (func_fail_ref) {
        napi_delete_reference(env, func_fail_ref);
    }
}

void NapiPrintListener::OnSuccess(int proc, int total) {
    napi_value undefined = nullptr;
    napi_value callResult = nullptr;
    napi_value args[2] = {nullptr};
    napi_value func_succ;

    RECPTPRINTER_HILOGE(RECPTPRINTER_JS_NAPI, "observer func_succ = %{public}p", func_succ_ref);

    DEBUG_NAPI_CALL_NO_RET(env, napi_create_int32(env, proc, &args[0]));
    DEBUG_NAPI_CALL_NO_RET(env, napi_create_int32(env, total, &args[1]));

    DEBUG_NAPI_CALL_NO_RET(env, napi_get_undefined(env, &undefined));
    DEBUG_NAPI_CALL_NO_RET(env, napi_get_reference_value(env, func_succ_ref, &func_succ));

    DEBUG_NAPI_CALL_NO_RET(env, napi_call_function(env, nullptr, func_succ, 2, args, &callResult));
}

void NapiPrintListener::OnFailed(int code, const std::string& err) {
    napi_value undefined = nullptr;
    napi_value callResult = nullptr;
    napi_value args[2] = {nullptr};
    napi_value func_fail;

    napi_create_int32(env, code, &args[0]);
    napi_create_string_utf8(env, err.c_str(), NAPI_AUTO_LENGTH, &args[1]);

    DEBUG_NAPI_CALL_NO_RET(env, napi_get_undefined(env, &undefined));
    DEBUG_NAPI_CALL_NO_RET(env, napi_get_reference_value(env, func_fail_ref, &func_fail));
    DEBUG_NAPI_CALL_NO_RET(env, napi_call_function(env, nullptr, func_fail, 2, args, &callResult));
}
}  // namespace Finance
}  // namespace SwanLink